Shadowing es un concepto que es imposible de evitar. Incluso si no conoces el término es 99.999% seguro que lo hayas implementado alguna vez en tu código.
Si lo traduces al español te queda algo como “sombrear” o “ensombrecer”. Pero probablemente sea mejor entenderlo como “ocultar”.
En términos técnicos se refiere al hecho de hacer que una variable en un scope mayor se vuelva inaccesible. Ocurriendo esto cuando se utiliza una variable con el mismo nombre en un scope menor.
Pero ¡ojo! Hacer shadowing no es una buena práctica, no es un estilo o técnica de programación. Es un concepto, es algo que ocurre y que por lo general deberías evitar.
Lo que debes saber antes
Para entender bien el concepto de shadowing, necesitas entender lo que es el lexical scope en Javascript y como funciona. Si quieres revisarlo o repasarlo puedes aprender acerca de Las 3 partes en las que se divide el Lexical Scope de Javascript.
Sin embargo si tienes prisa por entender que es esto del shadowing te puedo dar la explicación rápida.
El lexical scope se refiere al alcance que tiene una variable en un programa y en JS este se divide en 3 tipos: global, local y block. Una variable no es accessible fuera del scope donde fue declarada.
Shadowing
Caso Base
Como siempre, un ejemplo te ayudará a visualizarlo mejor.
const exerciseRoutine = {
description: 'pushups',
weekDays: [1,3,5]
}
/**
* Este código salva una rutina de ejercicio para un usuario
* @param {*} inputRoutine
*/
function saveRoutine (inputRoutine) {
console.log('exerciseRoutine = ', exerciseRoutine)
console.log('inputRoutine = ', inputRoutine)
//TODO: Save routine to database
}
const exampleInputRoutine = {
description: 'situps',
weekDays: [1,2,3,4,5]
}
saveRoutine(exampleInputRoutine)
Este código contiene una variable global y una local:
exerciseRoutine
– variable globalinputRoutine
– variable local de la funciónsaveRoutine
Como ya sabes la variable global es accesible, desde cualquier lado y porsupuesto la variable local solo desde dentro de su función, por lo que si corres este código se imprimirán ambos logs sin problemas.
exerciseRoutine = { description: 'pushups', weekDays: [ 1, 3, 5 ] }
inputRoutine = { description: 'situps', weekDays: [ 1, 2, 3, 4, 5 ] }
Shadowing en acción
Ahora digamos que decides renombrar el parámetro de inputRoutine
a exerciseRoutine
, tal vez así te lo pidieron, tal vez a ti se te ocurrió porque sientes que se ve mejor así. no importa, tu lo cambias. entonces ahí es cuando se presenta el shadowing.
const exerciseRoutine = {
description: 'pushups',
weekDays: [1,3,5]
}
/**
* Este código salva una rutina de ejercicio para un usuario
* @param {*} inputRoutine
*/
function saveRoutine (exerciseRoutine) {
console.log('exerciseRoutine = ', exerciseRoutine)
//TODO: Save routine to database
}
const exampleInputRoutine = {
description: 'situps',
weekDays: [1,2,3,4,5]
}
saveRoutine(exampleInputRoutine)
Como puedes ver ahora cuando mandamos a llamar la variable de exerciseRoutine
, se hace referencia a la variable local de la función, en lugar de a la global.
exerciseRoutine = { description: 'situps', weekDays: [ 1, 2, 3, 4, 5 ] }
El problema viene con lo siguiente… Ahora ¿cómo accedes al valor de la variable global?
Al menos dentro de la función saveRoutine
… ya no puedes.
La referencia a la variable global exerciseRoutine
se ha vuelto inaccesible ya que cuenta con una variable local que se llama igual. Podrías decir que la variable local lanzó una sombra que oculta la referencia a la variable global. De ahí que se le haya puesto al concepto: shadowing.
BONUS
Ahora la verdad es que hace un momento te mentí. Si hay una forma de acceder a una variable después de haberle hecho shadowing…de hecho hay varias.
El tema es que solo funcionan en ciertos escenarios:
- Necesitas haber declarado la variable con var o haber creado una función explícitamente con function.
- Necesitas una keyword específica que varía dependiendo del ambiente de Javascript que estés usando.
- Necesitas haber declarado la variable de manera global. Esto no funciona para shadowing en variables locales o de bloque.
Entre las keywords que podrías usar, se encuentran:
window
- Debes de estar corriendo el código Javascript en un navegador
window.exerciseRoutine
self
- Debes de estar corriendo el código Javascript en un Web Worker
self.exerciseRoutine
global
- Debes de estar corriendo el código Javascript en Node.js
global.exerciseRoutine
globalThis
- Debes de estar utilizando ES2020 o superior
globalThis.exerciseRoutine
El tema del shadowing es en esencia muy simple. Sin embargo si empiezas a considerar que los comportamientos cambian si declaraste una variable con let
/const
vs haberla declarado con var
, y que hay diferentes formas de manejarlo dependiendo del ambiente…entonces si se va haciendo más complejo.
Al final la regla sigue siendo la misma. Evita hacer shadowing. No es buena práctica, no es claro y te ahorrará muchos dolores de cabeza.